﻿#include "framework.h"
#include <mmsystem.h>
#include <time.h>
#include "Game.h"
#include "Resource.h"

// 函数声明
static void Game_Delete(Game*);
static void Game_CreateApple(Game*);
static void Game_DrawGameBoard(Game*);
static void Game_GameOver(Game*, BOOL);
static BOOL Game_IsWon(Game*);
static BOOL Game_OnKeyPress(Game*, UINT);
static void Game_OnTimer(Game*);
static void Game_Pause(Game*);
static BOOL Game_PlayMusic(Game*);
static BOOL Game_PointIsUsed(Game*, POINT);
static void Game_Start(Game*);

/**
 * 创建游戏。
 * @param callback  回调
 * @param drawer    绘图者
 * @param gameBoard 游戏区域
 * @return 游戏
 */
Game* New_Game(HWND callback, HDC drawer, HWND gameBoard) {
    Game* game = (Game*)calloc(1, sizeof(Game));

    // 初始化数据
    game->mCallback = callback;
    game->mDifficulty = 1;
    game->mDrawer = drawer;
    game->mGameBoard = gameBoard;
    game->mStatus = END;

    // 初始化函数
    game->deleteSelf = Game_Delete;
    game->onKeyPressed = Game_OnKeyPress;
    game->onTimer = Game_OnTimer;
    game->pause = Game_Pause;
    game->playMusic = Game_PlayMusic;
    game->start = Game_Start;

    // 重绘游戏区域
    Game_DrawGameBoard(game);

    // 置随机数种子
    srand((UINT)time(NULL));

    return game;
}

/**
 * 删除游戏。
 * @param game  游戏
 */
static void Game_Delete(Game* game) {
    free(game);
}

/**
 * 在随机位置创建果子。
 * @param game  游戏
 */
static void Game_CreateApple(Game* game) {
    POINT point = { 0 };
    BOOL used = FALSE;

    // 创建果子
    do {
        // 取 x、y 的随机值
        point.x = (rand() % (GAME_BOARD_GRID_COUNT_H - 2) + 1);
        point.y = (rand() % (GAME_BOARD_GRID_COUNT_V - 2) + 1);
        used = Game_PointIsUsed(game, point);
    } while (used);
    game->mApple = New_SnakeBody(game->mDrawer, point, APPLE);
}

/**
 * 画游戏区域。
 * @param game  游戏
 */
static void Game_DrawGameBoard(Game* game) {
    RECT rect = { 0, 0, GAME_BOARD_WIDTH, GAME_BOARD_HEIGHT };
    SetBkColor(game->mDrawer, GAME_BOARD_COLOR);
    ExtTextOut(game->mDrawer, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
}

/**
 * 游戏结束。
 * @param game  游戏
 * @param isWon 胜利则为 TRUE，否则为 FALSE
 */
static void Game_GameOver(Game* game, BOOL isWon) {
    // 将状态设置为已结束
    game->mStatus = END;

    // 停止定时器
    KillTimer(game->mCallback, TIMER_ID);

    // 停止播放音乐
    if (game->mEnableMusic) {
        PlaySound(NULL, NULL, 0);
    }

    // 发送通知
    SendMessage(game->mCallback, UM_GAME_OVER, isWon, 0);
}

/**
 * 判断是否胜利。
 * @param game  游戏
 * @return 胜利则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_IsWon(Game* game) {
    return game->mScore >= WIN_NUMBER;
}

/**
 * 按键事件的响应函数。
 * @param game  游戏
 * @param key   按键
 * @return 事件在此被处理则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_OnKeyPress(Game* game, UINT key) {
    if (game->mStatus != PLAYING) {
        return FALSE;
    }
    if (key >= VK_LEFT && key <= VK_DOWN) {
        game->mSnake->turn(game->mSnake, key);
        return TRUE;
    }
    return FALSE;
}

/**
 * 定时器事件的响应函数。
 * @param game  游戏
 */
static void Game_OnTimer(Game* game) {
    UINT elapse = 0;

    // 移动蛇
    BOOL isDead = game->mSnake->move(game->mSnake);
    if (isDead) {
        Game_GameOver(game, FALSE);
        return;
    }

    // 检查是否碰到果子
    if (game->mSnake->mHead->mPosition.x == game->mApple->mPosition.x &&
        game->mSnake->mHead->mPosition.y == game->mApple->mPosition.y) {
        // 碰到果子，将果子加入蛇链表
        game->mSnake->eat(game->mSnake, game->mApple);
        Game_CreateApple(game);
        game->mScore++;
        SendMessage(game->mCallback, UM_SCORE, 0, 0);

        // 判断是否胜利
        if (Game_IsWon(game)) {
            Game_GameOver(game, TRUE);
            return;
        }

        // 判断是否达到增加难度的条件
        if (game->mScore % DIFF_BASE == 0 && game->mDifficulty != DIFF_MAX) {
            game->mDifficulty++;
            SendMessage(game->mCallback, UM_DIFFICULTY_UP, 0, 0);
            KillTimer(game->mCallback, TIMER_ID);
            elapse = TIMER_INIT - TIMER_GROW * (game->mDifficulty - 1);
            SetTimer(game->mCallback, TIMER_ID, elapse, NULL);
        }
    }

    // 通知游戏区域重绘
    InvalidateRect(game->mGameBoard, NULL, FALSE);
}

/**
 * 游戏暂停。
 * @param game  游戏
 */
static void Game_Pause(Game* game) {
    if (game->mStatus == PLAYING) {
        // 停止定时器
        KillTimer(game->mCallback, TIMER_ID);

        // 停止播放音乐
        if (game->mEnableMusic) {
            PlaySound(NULL, NULL, 0);
        }

        // 将状态设置为已暂停
        game->mStatus = PAUSED;
    } else if (game->mStatus == PAUSED) {
        // 开启定时器
        SetTimer(game->mCallback, TIMER_ID, TIMER_INIT - TIMER_GROW * (game->mDifficulty - 1), NULL);

        // 播放音乐
        if (game->mEnableMusic) {
            PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
        }

        // 将状态设置为游戏中
        game->mStatus = PLAYING;
    }
}

/**
 * 播放音乐。
 * @param game  游戏
 * @return 正在播放音乐则为 TRUE，否则为 FALSE
 */
static BOOL Game_PlayMusic(Game* game) {
    game->mEnableMusic = !game->mEnableMusic;

    if (game->mEnableMusic) {
        if (game->mStatus == PLAYING) {
            PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
        }
    } else {
        PlaySound(NULL, NULL, 0);
    }

    return game->mEnableMusic;
}

/**
 * 判断某个点是否被占用。
 * @param game  游戏
 * @param point 点
 * @return 被占用了则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_PointIsUsed(Game* game, POINT point) {
    // 检查点是否在蛇上
    if (game->mSnake != NULL) {
        if (game->mSnake->pointIsOnMe(game->mSnake, point)) {
            return TRUE;
        }
    }

    // 检查点是否在果子上
    if (game->mApple != NULL) {
        return point.x == game->mApple->mPosition.x && point.y == game->mApple->mPosition.y;
    }

    return FALSE;
}

/**
 * 游戏开始。
 * @param game  游戏
 */
static void Game_Start(Game* game) {
    // 重绘游戏区域
    Game_DrawGameBoard(game);

    // 创建蛇
    if (game->mSnake != NULL) {
        game->mSnake->deleteSelf(game->mSnake);
    }
    game->mSnake = New_Snake(game->mDrawer);

    // 创建果子
    if (game->mApple != NULL) {
        game->mApple->deleteSelf(&game->mApple);
    }
    Game_CreateApple(game);

    // 通知游戏区域重绘
    InvalidateRect(game->mGameBoard, NULL, FALSE);

    // 重置难度
    game->mDifficulty = 1;

    // 重置分数
    game->mScore = 0;

    // 开启定时器
    KillTimer(game->mCallback, TIMER_ID);
    SetTimer(game->mCallback, TIMER_ID, TIMER_INIT, NULL);

    // 播放音乐
    if (game->mEnableMusic) {
        PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
    }

    // 将状态设置为游戏中
    game->mStatus = PLAYING;
}
